package pl.enigmatic.parser.value;

import java.util.HashMap;
import java.util.Map;

import pl.enigmatic.util.Reflection;
import pl.enigmatic.util.Strings;
import pl.enigmatic.parser.string.Converter;


public abstract class ValueConverter<S, T> implements pl.enigmatic.ValueConverter<S, T> {

	private final static String DOT = ".";
	@SuppressWarnings("rawtypes")
	private final static pl.enigmatic.ValueConverter identity = new pl.enigmatic.ValueConverter.IdentityImpl();

	public static final String getDefaultConverterName(Class<?> src, Class<?> trg) {
		src = Reflection.wrapper(src);
		trg = Reflection.wrapper(trg);
		return ValueConverter.class.getPackage().getName() + DOT + src.getPackage() + DOT + Strings.firstLower(src.getSimpleName()) + DOT
				+ trg.getName() + src.getSimpleName() + ValueConverter.class.getSimpleName();
	}

	@SuppressWarnings("rawtypes")
	private final static Map<String, pl.enigmatic.ValueConverter> converters = new HashMap<String, pl.enigmatic.ValueConverter>();

	@SuppressWarnings({ "unchecked" })
	public static final <S, T> pl.enigmatic.ValueConverter<S, T> createCustomConverter(
			final Class<? extends pl.enigmatic.ValueConverter<T, S>> cls) {
		final String name = cls.getName();
		if (!converters.containsKey(name)) {
			converters.put(name, Reflection.newInstance(cls));
		}
		return converters.get(name);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final pl.enigmatic.ValueConverter createCustomConverter(final String clazz) {
		final Class cls = Reflection.classForName(clazz);
		if (cls != null) {
			return createCustomConverter(cls);
		} else {
			return null;
		}
	}

	@SuppressWarnings({ "unchecked" })
	public static final <S, T> pl.enigmatic.ValueConverter<S, T> createDefaultConverter(final Class<? extends S> src,
			final Class<? extends T> trg) {
		if (Reflection.wrapper(trg) == Reflection.wrapper(src)) { return identity; }
		String id = getDefaultConverterName(Reflection.wrapper(src), Reflection.wrapper(trg));
		final Class<?> cls = Reflection.classForName(id);
		if (cls != null) {
			return createCustomConverter((Class<? extends pl.enigmatic.ValueConverter<T, S>>) cls);
		} else {
			pl.enigmatic.ValueConverter<S, T> res = null;
			if (String.class.equals(src)) {
				res = (pl.enigmatic.ValueConverter<S, T>) Converter.createDefaultConverter(trg.getName());
			} else if (String.class.equals(trg)) {
				res = (pl.enigmatic.ValueConverter<S, T>) Converter.createDefaultConverter(src.getName());
			} else {
				return null;
			}
			converters.put(id, res);
			id = getDefaultConverterName(trg, src);
			if (res == null) {
				converters.put(id, null);
			} else {
				converters.put(id, new ReversingOrderImpl<T, S>(res));
			}
			return converters.get(id);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static final pl.enigmatic.ValueConverter createDefaultConverter(final String src, final String trg) {
		final Class srccls = Reflection.classForName(src);
		final Class trgcls = Reflection.classForName(trg);
		if (srccls != null && trgcls != null) {
			return createDefaultConverter(srccls, trgcls);
		} else {
			return null;
		}
	}

	public static class ReversingOrderImpl<S, T> extends ValueConverter<S, T> {

		private final pl.enigmatic.ValueConverter<T, S> reversed;

		protected ReversingOrderImpl(final pl.enigmatic.ValueConverter<T, S> reversed) {
			this.reversed = reversed;
		}

		@Override
		public final T convert(final S value) {
			return reversed.convertBack(value);
		}

		@Override
		public final S convertBack(final T value) {
			return reversed.convert(value);
		}
	}
}
